home *** CD-ROM | disk | FTP | other *** search
- /*##########################################################################
- Copyright 2009 Tim Reid
-
- This file is part of the Stack Overflow Reputation Display extension
- for Mozilla Firefox.
-
- Stack Overflow Reputation Display is free software: you can
- redistribute it and/or modify it under the terms of the GNU General
- Public License as published by the Free Software Foundation, either
- version 3 of the License, or (at your option) any later version.
-
- Stack Overflow Reputation Display is distributed in the hope that it
- will be useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public
- License along with Stack Overflow Reputation Display. If not,
- see <http://www.gnu.org/licenses/>.
- ##########################################################################*/
-
- var sorepdisplay = {
- myid: "sorepdisplay@firefox.twistedlip.org",
-
- global: {
- accounts: [],
- },
- displays: null,
- debug: false,
-
- interfaces: {
- prefs: Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefService)
- .getBranch("extensions.sorepdisplay."),
- sound: Components.classes["@mozilla.org/sound;1"]
- .createInstance(Components.interfaces.nsISound),
- wm: Components.classes["@mozilla.org/appshell/window-mediator;1"]
- .getService(Components.interfaces.nsIWindowMediator),
- console: Components.classes["@mozilla.org/consoleservice;1"]
- .getService(Components.interfaces.nsIConsoleService),
- },
-
- observe: function (aSubject, aTopic, aData) {
- if (aTopic == "nsPref:changed") {
- if (0) {
- } else if (aData == "config") {
- var newconfig = this.interfaces.prefs.getCharPref("config");
- if (newconfig != this.global.currentconfig) {
- //################################################################
- // See below for why this setTimeout() is needed.
- //################################################################
- var me = this;
- setTimeout(function () {
- me.redoconfig(newconfig);
- me.global.currentconfig = newconfig;
- }, 1);
- }
- } else if (aData == "response") {
- var responsetext = this.interfaces.prefs.getCharPref("response");
- for (var i=0; i<this.global.accounts.length; i++)
- this.global.accounts[i].handleresponse(responsetext);
- } else if (aData == "reputationtextcolor") {
- this.reputationtextcolor = this.interfaces.prefs.getCharPref("reputationtextcolor");
- this.settextcolors();
- } else if (aData == "badgetextcolor") {
- this.badgetextcolor = this.interfaces.prefs.getCharPref("badgetextcolor");
- this.settextcolors();
- } else if (aData == "debug") {
- this.debug = this.interfaces.prefs.getBoolPref("debug");
- for (var i=0; i<this.global.accounts.length; i++)
- this.global.accounts[i].debug = this.debug;
- } else {
- alert("Change in preference " + aData);
- }
- } else if (aTopic == "soaccount:scorechanged") {
- if (this.debug)
- this.interfaces.console.logStringMessage("SORepDisplay handling score change...");
- for (var i=0; i<this.displays.length; i++)
- if (this.displays[i].account === aSubject)
- this.displays[i].updatedetails();
- }
- },
-
- //##########################################################################
- // Why is the setTimeout() needed?
- //
- // In its absence, when the configuration is changed using the preferences
- // dialog, the XMLHttpRequest objects generated during the redoconfig()
- // call get cancelled before they can complete. This is probably because
- // the observe() method is called by the same thread which is handling the
- // dialog interaction, but objects associated with that thread get taken
- // down when the dialog closes. Evidence supporting this explanation:
- // a delay introduced at *any* point between the XMLHttpRequest send()
- // call and the return from the observe() call is sufficient to allow the
- // XMLHttpRequest to complete correctly.
- //
- // The workaround to this problem is to call the redoconfig() method as the
- // result of a setTimeout() call. This fixes the problem, probably because
- // it allows the XMLHttpRequest to be set up in a different context, and
- // therefore not cancelled.
- //##########################################################################
-
- doOptions: function () {
- if (this.debug)
- this.interfaces.console.logStringMessage("SORepDisplay opening configuration dialog...");
- window.openDialog("chrome://sorepdisplay/content/options.xul",
- "options",
- "chrome,modal,centerscreen");
- },
-
- launchtab: function (url) {
- if (this.debug)
- this.interfaces.console.logStringMessage("SORepDisplay opening new tab (" + url + ")...");
- var numTabs = gBrowser.browsers.length;
- for (var index = 0; index < numTabs; index++)
- if (gBrowser.getBrowserAtIndex(index).currentURI.spec == url)
- break;
-
- if (index < numTabs) {
- gBrowser.selectedTab = gBrowser.tabContainer.childNodes[index];
- gBrowser.getBrowserForTab(gBrowser.selectedTab).reload();
- } else {
- gBrowser.selectedTab = gBrowser.addTab(url);
- }
- },
-
- configureaccounts: function (config) {
- var configparts = config.split(/,/);
-
- var soundbase = "chrome://sorepdisplay/content/audio/";
- var sounduris = {
- up: soundbase + "31189__acclivity__Triangle1.part.wav",
- down: soundbase + "66127__jobro__jobromedia_bullet_slowdown.lq.wav",
- };
-
- var accounts = [];
- for (var i=0; i<configparts.length; i++) {
- if (configparts[i][0] == "#")
- continue;
-
- var account = new SOAccount(configparts[i]);
- account.sounduris = sounduris;
- account.debug = this.debug;
- account.launchtimer();
- account.addObserver(this);
- accounts.push(account);
- }
-
- return accounts;
- },
-
- removedisplays: function () {
- if (!this.displays)
- return;
-
- while (this.displays.length > 0) {
- var display = this.displays.shift();
- display.elements.panel.parentNode.removeChild(display.elements.panel);
- display.elements.popupset.parentNode.removeChild(display.elements.popupset);
- }
- },
-
- adddisplays: function (accounts) {
- var displays = [];
-
- var insertpoint = document.getElementById("sorepdisplay-insertpoint");
- var insertbefore = insertpoint.nextSibling;
- for (var i=0; i<accounts.length; i++) {
- var account = accounts[i];
- var display = new SODisplay({
- account: account,
- });
-
- insertpoint.parentNode.insertBefore(display.elements.panel, insertbefore);
- insertpoint.parentNode.insertBefore(display.elements.popupset, insertbefore);
-
- display.updatedetails();
-
- var me = this;
- display.elements.menuitems.options.addEventListener("command", function (e) { me.doOptions(e); }, true);
- display.registeropener(function (url) { me.launchtab(url); } );
-
- displays.push(display);
- }
-
- return displays;
- },
-
- removeaccounts: function () {
- for (var i=0; i<this.global.accounts.length; i++)
- this.global.accounts[i].cleartimer();
- this.global.accounts = [];
- },
-
- redoconfig: function (config) {
- this.removeaccounts();
- this.global.accounts = this.configureaccounts(config);
- this.global.accountswantingpageloads = this.findaccountswantingpageloads();
- var instances = this.findinstances();
- for (var i=0; i<instances.length; i++) {
- instances[i].removedisplays();
- instances[i].displays = instances[i].adddisplays(this.global.accounts);
- instances[i].initpageloads();
- }
- },
-
- findinstances: function () {
- var instances = [];
- var enumerator = this.interfaces.wm.getEnumerator("navigator:browser");
- while(enumerator.hasMoreElements()) {
- var win = enumerator.getNext();
- if (win.sorepdisplay)
- instances.push(win.sorepdisplay);
- }
-
- return instances;
- },
-
- findotherinstances: function () {
- var candidates = this.findinstances();
- var instances = [];
- for (var i=0; i<candidates.length; i++)
- if (candidates[i] !== this)
- instances.push(candidates[i]);
- return instances;
- },
-
- pageload: function (e) {
- var changed = false;
- var candidates = this.global.accountswantingpageloads;
- for (var i=0; i<candidates.length; i++)
- changed = changed | candidates[i].pageload(e.originalTarget);
- if (changed) {
- this.global.currentconfig = this.config;
- this.interfaces.prefs.setCharPref("config", this.global.currentconfig);
-
- this.global.accountswantingpageloads = this.findaccountswantingpageloads();
- var instances = this.findinstances();
- for (var i=0; i<instances.length; i++)
- instances[i].initpageloads();
- }
- },
-
- initpageloads: function () {
- var appcontent = document.getElementById("appcontent");
- if(appcontent) {
- var me = this;
- if (this.global.accountswantingpageloads.length > 0) {
- this.listener = function (e) { me.pageload(e); };
- appcontent.addEventListener("DOMContentLoaded",
- this.listener,
- false);
- } else {
- appcontent.removeEventListener("DOMContentLoaded",
- this.listener,
- false);
- }
- }
- },
-
- findaccountswantingpageloads: function () {
- var accounts = []
- for (var i=0; i<this.global.accounts.length; i++)
- if (this.global.accounts[i].wantspageloads())
- accounts.push(this.global.accounts[i]);
- return accounts;
- },
-
- enablesounds: function (b) {
- for (var i=0; i<this.global.accounts.length; i++)
- this.global.accounts[i].dosounds = b;
-
- var instances = this.findinstances();
- for (var i=0; i<instances.length; i++)
- for (var j=0; j<instances[i].displays.length; j++)
- instances[i].displays[j].dosounds = b;
- },
-
- soundmenu: function (e) {
- var newvalue = e.target.getAttribute("checked") == "true";
- this.interfaces.prefs.setBoolPref("soundeffects", newvalue);
- },
-
- get config () {
- var configs = [];
- for (var i=0; i<this.global.accounts.length; i++)
- configs.push(this.global.accounts[i].config);
- return configs.join(",");
- },
-
- close: function (e) {
- for (var i=0; i<this.global.accounts.length; i++)
- this.global.accounts[i].removeObserver(this);
- },
-
- settextcolors: function () {
- if (this.stylesheet) {
- for (var i=0; i<this.stylesheet.cssRules.length; i++)
- if (this.stylesheet.cssRules[i].type == CSSRule.STYLE_RULE &&
- this.stylesheet.cssRules[i].selectorText == ".reputationtextcolor")
- this.stylesheet.deleteRule(i);
-
- for (var i=0; i<this.stylesheet.cssRules.length; i++)
- if (this.stylesheet.cssRules[i].type == CSSRule.STYLE_RULE &&
- this.stylesheet.cssRules[i].selectorText == ".badgetextcolor")
- this.stylesheet.deleteRule(i);
-
- this.stylesheet.insertRule(".reputationtextcolor { color: " + this.reputationtextcolor + "; }", 0);
- this.stylesheet.insertRule(".badgetextcolor { color: " + this.badgetextcolor + "; }", 0);
- }
- },
-
- init: function (e) {
- this.debug = this.interfaces.prefs.getBoolPref("debug");
- this.reputationtextcolor = this.interfaces.prefs.getCharPref("reputationtextcolor");
- this.badgetextcolor = this.interfaces.prefs.getCharPref("badgetextcolor");
- var instances = this.findotherinstances();
-
- if (instances.length > 0) {
- this.global = instances[0].global;
- } else {
- this.global.currentconfig = this.interfaces.prefs.getCharPref("config");
- this.global.accounts = this.configureaccounts(this.global.currentconfig);
- this.global.accountswantingpageloads = this.findaccountswantingpageloads();
- }
-
- this.removedisplays();
- this.displays = this.adddisplays(this.global.accounts);
- this.initpageloads();
-
- var me = this;
-
- this.interfaces.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
- this.interfaces.prefs.addObserver("", this, false);
-
- var stylesheets = document.styleSheets;
- for (var i=0; i<stylesheets.length; i++)
- if (stylesheets[i].href == "chrome://sorepdisplay/skin/sorepdisplay.css")
- this.stylesheet = stylesheets[i];
-
- this.settextcolors();
- },
- };
-
- window.addEventListener("load", function (e) { sorepdisplay.init(e); }, false);
- window.addEventListener("unload", function (e) { sorepdisplay.close(e); }, false);
-